package SSF.OS.OSPFv2.test;

/** PacketGenerator.java
  * 
  * @author Dirk Jacob
  * @version 0.2.1
  */

import com.renesys.raceway.DML.*;
import SSF.OS.*;
import SSF.OS.OSPFv2.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import java.util.*;
  
/** This is a replacement for the SSF.OS.IP class, which is able to manipulate 
  * packets flowing from and to an OSPF session. The way, the packets are manipulated
  * are specified by some predefined test behaviors -- each used in certain test cases
  * of the OSPF test suite.<br>
  * The session can be configured as follows:
  * <pre>
  *   ProtocolSession [ name ip use SSF.OS.OSPF.test.PacketGenerator
  *       debug &lt;no|log|error|all&gt;
  *       test [
  *           interface &lt;id&gt;
  *           behavior &lt;behavior-id&gt;
  *       ]
  *   ]
  * </pre>
  * Each interface of the router may have one behavior associated with it in a 
  * separate <tt>test</tt> attribute. The behavior-id can be one of the following:
  * <ul>
  * <li><b>default</b>: Normal OSPF behavior.
  * <li><b>old_lsa_rcpt</b>: Handling of the receipt of old LSAs (Test 2.6)
  * <li><b>nbr_state_too_low</b>: Correct handling of LSRequests from neighbors in a state
  * lesser than EXCHANGE (Test 2.7)
  * <li><b>dd_retransmit1-4</b>: Retransmission of database description packets (Test 2.8)
  * <li><b>event_seq_number_mismatch1-8</b>: Verify the generation of the SequenceNumberMismatch
  * event (Test 2.9)
  * <li><b>lsa_request_reransmit1-2</b>: Retransmission of LSRequest packets (Test 2.17)
  * <li><b>event_bad_ls_req1-2</b>: Correct handling of the BadLSReq event (Test 2.18)
  * <li><b>remove_lsa_from_retransmission_list</b>: Removing LSAs from the retransmission list,
  * when they are discarded from the link state database (Test 2.21)
  * <li><b>ls_sequence_number_wrap</b>: Correct behavior, when the LS sequence number wraps 
  * (Test3.22)
  * </ul>
  * For a more detailed description of the tests, see the additional documentation of the
  * test package.
  */
public class PacketGenerator extends IPwithErrorInjection {

	/** This is a generalized timer class, which can be used to execute delayed
	  * actions for a specific test behavior. The timer has the test behavior 
	  * and some behavior specific data associated with it and call a central
	  * event dispatcher, when it fires. This dispatcher then executes action
	  * routines dependent of the specified behavior.
	  */
	class BehaviorTimer extends SSF.OS.Timer {
		
		/** Data associated with this timer. In general, this is data associated with
		  * a neighbor.
		  */
		private Object data;
		/** Behavior id */
		private int behavior;
		
		/** Constructor for the behavior timer.
		  * @param e The ProtocolGraph, this PacketGenerator is belonging to.
		  * @param dt Timer interval, in milliseconds (?).
		  * @param be Behavior id, for the determination of the action routine.
		  * @param da An arbitrary Object containing parameters for the action Routine.
		  *           This routine will cast the Object to the right data type.
		  */
		public BehaviorTimer(ProtocolGraph e, long dt, int be, Object da) {
			super(e, dt);
			
			behavior = be;
			data = da;
		}

		/** Callback method, which calls the central event dispatcher. */
		public void callback() {
			timerEvent( behavior, data );
		}
	}
	
	/** these attributes contain information about the actual state
	    of the adjacency, which triggers certain special behaviors */   
	class NeighborState {
		/** the neighbor's router id */
		int nbrRouterID;    
		/** the neighbor state */
		int nbrState;
		/** the neghbor's network mask */       
		int nbrMask;
		/** number of dd packets sent to this neighbor */        
		int ddPacketsSent;
		/** number of dd packets received from this neighbor */  
		int ddPacketsRcvd;  
		/** M-bit of the last received DD packet */
		boolean lastMBitRcvd;
		/** M-bit of the last sent DD packet */
		boolean lastMBitSent;
		/** vector with all LSAs received from this neighbor */
		Vector lsasFromNbr; 
		/** interface attached to the link to this neighbor */
		int iface;          
		/** Timer to trigger certain events in a behavior */
		SSF.OS.Timer timer;	
		/** The area ID, this neighbor/interface belongs to */
		int areaId;			
		/** The own interface's IP address */
		int ifIP;			
		/** The neighbor's IP address */
		int nbrIP;	
		/** The neighbor's NH address */
		String nbrNh;		
		/** Behavior internal variable that can be used for different purposes in the different behaviors. */
		int behaviorState;
		/** The time, when the last non-hello packet has been heard from the neighbor */
		double lastNonHelloHeard;
		/** The time, when the last non-hello packet has been sent to the neighbor */
		double lastNonHelloSent;
		/** The time, when the last hello packet has been heard from the neighbor */
		double lastHelloHeard;
		/** The time, when the last hello packet has been sent to the neighbor */
		double lastHelloSent;
		/** Neighbor router LSA */
		LSA nbrRouterLsa;
		/** First router LSA heard from the neighbor */
		LSA firstNbrRouterLsa;
		/** DD Sequence number */
		int ddSequence;
		/** DD I Bit */
		boolean ddInit;
		/** DD M Bit */
		boolean ddMore;
		/** DD MS Bit */
		boolean ddMaster;
		/** DD MTU */
		int ddMTU;
		/** DD options */
		boolean[] ddOptions;
		/** Last DD Packet sent to the neighbor */
		DD_Packet lastDDPacket;
		/** Hashtable with requests sent to this neighbor */
		Hashtable requestedLSAs;
		
		/** Constructs a new neighbor state with fields initialized to default values. */
		public NeighborState() {
			lsasFromNbr = new Vector();
			nbrState = Neighbor.DOWN;
			ddPacketsSent = 0;
			ddPacketsRcvd = 0;
			lastMBitRcvd = true;
			lastMBitSent = true;
			iface = -1;
			areaId = 0;
			ifIP = 0;
			nbrIP = 0;
			behaviorState = 0;
			lastNonHelloHeard = 0.0;
			lastNonHelloSent = 0.0;
			lastHelloHeard = 0.0;
			lastHelloSent = 0.0;
			nbrRouterLsa = null;
			firstNbrRouterLsa = null;
			ddSequence = 0x0;		
			ddInit = false;
			ddMore = false;
			ddMaster = false;
			ddMTU = 1500;
			ddOptions = null;
			lastDDPacket = null;
			requestedLSAs = new Hashtable();
			nbrNh = "";
		}
	}
	
	/** this hashtable contains all neighbors being watched */
	private Hashtable neighbors;     
	
	/** the own router lsa */
	private LSA ownRouterLSA; 
	
	/** the own router ID */
	private int ownRouterID; 
	
	/** the optional capabilities of this router */
	private boolean[] options;
	
	/** pointer to the OSPF session rnning on this router */
	private OSPF ospfSession;
	
	/** vector with a set of LSAs sufficient for 5 dd packets */
	private Vector lsas;             
	
	/** this vector stores for each interface, which behavior it should show */
	private Vector behavior;  
	
	/** All the IP-addresses, we know of all neighbors */
	private Hashtable addresses;
		
	
	/** numeric id for default behavior */
	private static final int DEFAULT = 0x00;
	/** numeric id for old_lsa_rcpt */
	private static final int OLD_LSA_RCPT = 0x01;
	/** numeric id for nbr_state_too_low */
	private static final int NBR_STATE_TOO_LOW = 0x02;
	/** numeric id for dd_retransmit1 */
	private static final int DD_RETRANSMIT1 = 0x03;
	/** numeric id for event_seq_numer_mismatch1 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH1 = 0x04;
	/** numeric id for lsa_request_retransmit1 */
	private static final int LSA_REQUEST_RETRANSMIT1 = 0x05;
	/** numeric id for event_bad_ls_req1 */
	private static final int EVENT_BAD_LS_REQ1 = 0x06;
	/** numeric id for ls_sequence_number_wrap */
	private static final int LS_SEQUENCE_NUMBER_WRAP = 0x07;
	/** numeric id for event_bad_ls_req2 */
	private static final int EVENT_BAD_LS_REQ2 = 0x08;
	/** numeric id for dd_retransmit2 */
	private static final int DD_RETRANSMIT2 = 0x09;
	/** numeric id for dd_retransmit3 */
	private static final int DD_RETRANSMIT3 = 0x0A;
	/** numeric id for dd_retransmit4 */
	private static final int DD_RETRANSMIT4 = 0x0B;
	/** numeric id for event_seq_numer_mismatch2 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH2 = 0x0C;
	/** numeric id for event_seq_numer_mismatch3 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH3 = 0x0D;
	/** numeric id for event_seq_numer_mismatch4 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH4 = 0x0E;
	/** numeric id for event_seq_numer_mismatch5 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH5 = 0x0F;
	/** numeric id for event_seq_numer_mismatch6 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH6 = 0x10;
	/** numeric id for event_seq_numer_mismatch7 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH7 = 0x11;
	/** numeric id for event_seq_numer_mismatch8 */
	private static final int EVENT_SEQ_NUMBER_MISMATCH8 = 0x12;
	/** numeric id for lsa_request_retransmit2 */
	private static final int LSA_REQUEST_RETRANSMIT2 = 0x13;
	/** numeric id for remove_lsa_from_retransmission_list */
	private static final int REMOVE_LSA_FROM_RETRANSMISSION_LIST = 0x14;
	
	/** the labels for the behaviors in the DML configuration */
	private String behaviorLabel[] = { "default", "old_lsa_rcpt",
			"nbr_state_too_low", "dd_retransmit1", "event_seq_number_mismatch1",
			"lsa_request_retransmit1", "event_bad_ls_req1", "ls_sequence_number_wrap",
			"event_bad_ls_req2", "dd_retransmit2", "dd_retransmit3", "dd_retransmit4",
			"event_seq_number_mismatch2", "event_seq_number_mismatch3", 
			"event_seq_number_mismatch4", "event_seq_number_mismatch5",
			"event_seq_number_mismatch6", "event_seq_number_mismatch7",
			"event_seq_number_mismatch8", "lsa_request_retransmit2",
			"remove_lsa_from_retransmission_list" };	
	
	/** the log utility */
	private Logger log = null;
	
	/** Creates the packet generator and initializes internal data structures. */
	public PacketGenerator() {
		
		/* call the super-class' constructor first */
		super();
		
		/* create a new behavior vector */
		behavior = new Vector();
		
		/* now initialize the lsa vector and the neighbors hashtable */
		lsas = new Vector();
		neighbors = new Hashtable();
		
		addresses = new Hashtable();
		
		/* create default options */
		options = new boolean[8];
		for( int i = 0; i < 8; i++) {
			options[i] = false;
		}
		
		ospfSession = null;
		ownRouterID = 0;
		
		log = new Logger(Logger.DEBUG_NONE, this, "PacketGenerator");
	}
	
	/** Configure the packet generator according to the behavior descriptions from the
	  * DML configuration.
	  */
	public void config(Configuration cfg) 
		throws configException {
		
		/* first configure the super class */
		super.config( cfg );
				
		/* now configure the log utility */
		log.config( cfg );
		
		/* now go through the configuration and find all specified behaviors */
		for( Enumeration tests = cfg.find("test"); tests.hasMoreElements(); ) {
			Configuration t = (Configuration)tests.nextElement();
			
			String be = (String)t.findSingle("behavior");
			String intf = (String)t.findSingle("interface");
			
			/* When there's no interface id specified, do nothing */
			if(intf != null) {
				int id = Integer.parseInt( intf );
				int bi = behaviorFromString( be );
				
				/* eventually adjust the size of the interface vector */
				if(behavior.size() < id + 1) {
					behavior.setSize( id + 1 ); 
				} 
				
				/* now set the behavior for id to bi */
				behavior.set(id, new Integer( bi ));
				
				log.logMessage("Interface "+id+" has behavior "+be+".");
				if(bi == EVENT_SEQ_NUMBER_MISMATCH8) {
					/* TODO */
					log.logMessage("Behavior not implemented yet... OSPF doesn't support AS-external LSAs.");
				}
			}
		}
	}

	/** This method handles incoming packets. To determine the neighbor states, all packets
	  * are tracked, calling the track() method and then handled accordng to the specified 
	  * behavior. All non-OSPF packets are simply given to the super-class's push() method
	  * for further handling.
	  */
	public boolean push(ProtocolMessage message, ProtocolSession fromSession) 
		throws ProtocolException {
			
		/* when the first packet arrives, first initialize the pointer to the OSPF session */
		if( ospfSession == null ) {
			ospfSession = (OSPF)inGraph().SessionForName("ospf");	
		}
			
		boolean result;
		IpHeader ipHdr = (IpHeader)message;
		
		if(ipHdr.PROTOCOL_NO == Protocols.OSPF_PRTL_NUM) {
	
			/* first, update the state of the neihbors according to the packet which
		       has been pushed into the session */	
			track( message, fromSession );
			
			/* now call the special behavior packet handler */
			result = handle( message, fromSession );
		} else {
			/* non-OSPF packets are simply given to IP */
			result = super.push( message, fromSession );
		}
		
		return result;
	}
	
	/** This method keeps track of the neighbor's states dependent of the packets 
	  * which are received. It is responsible for the maintenance of the neighbors
	  * hashtable and the neighbor states contained in it. 
	  */
	private void track(ProtocolMessage message, ProtocolSession fromSession) {
		
		IpHeader ipHdr = (IpHeader)message;
		Packet ospfHeader;
		NeighborState associatedNeighbor;
		
		/* don't look at packets other than OSPF packets */
		if(ipHdr.PROTOCOL_NO == Protocols.OSPF_PRTL_NUM) {
			
			ospfHeader = (Packet)ipHdr.payload(); 
			
			if (ospfHeader != null) {
				/* for all OSPF packets, first check, if there is already a corresponding
			   	neighbor in the neighbor list */
				associatedNeighbor = getNeighbor(message, fromSession);
			
				/* we must distinguish two cases - is the packet coming from 'our' OSPF
		    	   Session or is it coming from a NIC? */
				if(fromSession instanceof NIC) {
					/* Packets coming from a NIC are handled different from packets from an
					   OSPF session. */
					   
					if(associatedNeighbor == null) {
					
						/* if there's no neighbor in the list, create a new entry */
						associatedNeighbor = new NeighborState();
						associatedNeighbor.nbrRouterID = ospfHeader.router_ID;
						associatedNeighbor.iface = associatedInterface(message, fromSession);				
						associatedNeighbor.nbrIP = ipHdr.SOURCE_IP;
						associatedNeighbor.nbrNh = ospfHeader.nh;
						associatedNeighbor.ifIP = ((NIC)fromSession).ipAddr;
						associatedNeighbor.areaId = ospfHeader.areaNum;
					
						neighbors.put(new Integer(ospfHeader.router_ID), associatedNeighbor);
						
						log.logMessage("Discovered new neighbor "+IP_s.IPtoString(associatedNeighbor.nbrRouterID)+" on interface "+associatedNeighbor.iface+".");
					}
					
					/* now add the neighbor's ipaddress to the data-structure if not already contained */
					Integer ad = new Integer(ipHdr.SOURCE_IP);
					if(!addresses.containsKey( ad )) {
		
						Integer ri = new Integer(associatedNeighbor.nbrRouterID);
						addresses.put( ad, ri );
						
						String as = IP_s.IPtoString(ipHdr.SOURCE_IP);
						String rs = IP_s.IPtoString(associatedNeighbor.nbrRouterID);
						log.logMessage("Found new IP-Address "+as+" for neighbor "+rs+".");
					}
					
					/* Look at the packet type to determine the action. */
					switch(ospfHeader.type) {
						case Packet.HELLO:
							/* Keep track of Hello Packets coming from the neighbor */
							
							/* Keep the lastHelloHeard field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastHelloHeard = getSimTime();
							}
							break;
						case Packet.DATABASE:
							/* Keep track of DD Packets coming from the neighbor */
							DD_Packet ddPkt = (DD_Packet)ospfHeader.payload();
							
							if(ddPkt.initialize) {
								associatedNeighbor.ddPacketsRcvd = 1;
								log.logMessage("Initial DD packet from "+IP_s.IPtoString(associatedNeighbor.nbrRouterID)+".");
							} else {
								associatedNeighbor.ddPacketsRcvd++;
								log.logMessage(associatedNeighbor.ddPacketsRcvd+". DD packet from "+IP_s.IPtoString(associatedNeighbor.nbrRouterID)+".");
							}
							
							if(associatedNeighbor != null) {
								/* Keep the lastNonHelloHeard field up to date. */
								associatedNeighbor.lastNonHelloHeard = getSimTime();
								
								/* Keep in mind if the MBit was set */
								associatedNeighbor.lastMBitRcvd = ddPkt.more;
							}
							
									
							break;
						case Packet.LSREQUEST:
							/* Keep track of LSRequest Packets coming from the neighbor */
							LS_RequestPacket reqPkt = (LS_RequestPacket)ospfHeader.payload();
							Vector requests = reqPkt.getRequests();
							
							for(Enumeration e = requests.elements(); e.hasMoreElements();) {
								
								String nextRequest = (String)e.nextElement();
								
								int be = nextRequest.indexOf(" ");
								int en = nextRequest.lastIndexOf(" ");
								
								String lsId = nextRequest.substring(be, en).trim();
								
								associatedNeighbor.requestedLSAs.put(lsId, nextRequest);
								log.logMessage("Neighbor requested "+lsId+" ("+nextRequest+")");
							}
							
							
							/* Keep the lastNonHelloHeard field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloHeard = getSimTime();
							}
							break;
						case Packet.LSUPDATE:
							/* Keep track of LSUpdate Packets coming from the neighbor */
							
							/* Keep the lastNonHelloHeard field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloHeard = getSimTime();
								
								/* check if the update contains the neighbor's router LSA */
								LS_UpdatePacket updPkt = (LS_UpdatePacket)ospfHeader.payload();
								Vector updLsas = updPkt.getLSAs();
							
								LSA tmpLsa = null;
							
								for(Enumeration e = updLsas.elements(); e.hasMoreElements();) {
									tmpLsa = (LSA)e.nextElement();
								
									if(tmpLsa.lsType == LSA.ROUTER_LSA) {
										/* first success - we found a router LSA */
									
										/* now check, if this is the neighbor's router LSA */
										if(tmpLsa.lsID == associatedNeighbor.nbrRouterID) {
											/* GOTCHA! */
											associatedNeighbor.nbrRouterLsa = (LSA)tmpLsa.copy();
										
											log.logMessage("Found neighbor's router LSA. Great.");
											
											if(associatedNeighbor.firstNbrRouterLsa == null) {
												associatedNeighbor.firstNbrRouterLsa = (LSA)tmpLsa.copy();
												log.logMessage("Saved first Router LSA heard from neighbor.");
											}
											
											
											/* when we found the LSA, leave the for loop */
											break;
										}
									}
								}
							}
							break;
						case Packet.LSACK:
							/* Keep track of LSAck Packets coming from the neighbor */
							
							/* Keep the lastNonHelloHeard field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloHeard = getSimTime();
							}
							break;
						default:
							/* Keep the lastNonHelloHeard field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloHeard = getSimTime();
							}
							break;
					}
				} else {
					/* If the packet is not from a NIC, assume it comes from an OSPF session.
				       Don't crosscheck this, because maybe there is another layer between OSPF
				       and the packet generator for testing. */     
					
					/* set the ownRouterID field */
					if(ownRouterID == 0) {
						ownRouterID = ospfHeader.router_ID;
						log.logMessage("Discovered my own router id:"+IP_s.IPtoString(ownRouterID)+".");
					}
			
					/* First look at the packet Type */
					switch(ospfHeader.type) {
						case Packet.HELLO:
							/* Keep track of Hello Packets coming from the OSPF Session */
							
							/* Keep the lastHelloSent field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastHelloSent = getSimTime();
							}
							break;
						case Packet.DATABASE:
							/* Keep track of DD Packets coming from the OSPF Session */
							DD_Packet ddPkt = (DD_Packet)ospfHeader.payload();
							
							if(associatedNeighbor != null) {
								if(ddPkt.initialize) {
									associatedNeighbor.ddPacketsSent = 1;
									log.logMessage("Initial DD packet from OSPF session.");
								} else {
									associatedNeighbor.ddPacketsSent++;
									log.logMessage(associatedNeighbor.ddPacketsSent+". DD packet from OSPF session.");
								}
								
								/* Keep the lastNonHelloSent field up to date. */
								associatedNeighbor.lastNonHelloSent = getSimTime();
								
								/* Look if the M-bit is set */
								associatedNeighbor.lastMBitSent = ddPkt.more;
								
								/* Store a copy of this packet */
								associatedNeighbor.lastDDPacket = (DD_Packet)ddPkt.copy();
								
								/* Store the DD packet's options */
								associatedNeighbor.ddOptions = (boolean[])ddPkt.options.clone();
								
								associatedNeighbor.ddMTU = ddPkt.interfaceMTU;
								associatedNeighbor.ddSequence = ddPkt.DD_seq_num;
								associatedNeighbor.ddMaster = ddPkt.isMaster;
								
							} else {
								log.logMessage("Cannot determine associated neighbor for DD packet - sorry!");
							}
							
							break;
						case Packet.LSREQUEST:
							/* Keep track of LSRequest Packets coming from the OSPF Session */
							
							/* Keep the lastNonHelloSent field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloSent = getSimTime();
							}
							break;
						case Packet.LSUPDATE:
							/* Keep track of LSUpdate Packets coming from the OSPF Session */
							
							/* Check, if the router's own router LSA is contained in the packet */
							LS_UpdatePacket updPkt = (LS_UpdatePacket)ospfHeader.payload();
							Vector updLsas = updPkt.getLSAs();
							
							LSA tmpLsa = null;
							
							for(Enumeration e = updLsas.elements(); e.hasMoreElements();) {
								tmpLsa = (LSA)e.nextElement();
								
								/* look, if the neighbor requested this LSA */
								String lsId = IP_s.IPtoString(tmpLsa.lsID);
								
								if(associatedNeighbor.requestedLSAs.containsKey(lsId)) {
									/* yes - we have... remove the key from the list */
									associatedNeighbor.requestedLSAs.remove(lsId);
									log.logMessage("Removing "+lsId+" from the list of requested LSAs.");
								}
								
								if(tmpLsa.lsType == LSA.ROUTER_LSA) {
									/* first success - we found a router LSA */
									
									/* now check, if this is our router LSA */
									if(tmpLsa.lsID == ownRouterID) {
										/* GOTCHA! */
										ownRouterLSA = (LSA)tmpLsa.copy();
										
										log.logMessage("Found my own router LSA. Great.");
										
										/* when we found the LSA, leave the for loop */
										break;
									}
								}
							}
							
							/* Keep the lastNonHelloSent field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloSent = getSimTime();
							}
							break;
						case Packet.LSACK:
							/* Keep track of LSAck Packets coming from the OSPF Session */
							
							/* Keep the lastNonHelloSent field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloSent = getSimTime();
							}
							break;
						default:
							/* Keep the lastNonHelloSent field up to date. */
							if(associatedNeighbor != null) {
								associatedNeighbor.lastNonHelloSent = getSimTime();
							}
							break;
					}
				}	
			}
		}
	}

	/** This method handles individual packets. Therefor it dispatches the packets
	    according to the active behavior */
	private boolean handle(ProtocolMessage message, ProtocolSession fromSession)
		throws ProtocolException {
		
		boolean result = false;
		int intf = associatedInterface( message, fromSession );
		int b = getBehavior( intf );
		
		switch( b ) {
			case OLD_LSA_RCPT:
				result = actionOldLsaRcpt( intf, message, fromSession );
				break;
			case NBR_STATE_TOO_LOW:
				result = actionNbrStateTooLow( intf, message, fromSession );
				break;
			case DD_RETRANSMIT1:
				result = actionDdRetransmit1( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH1:
				result = actionEventSeqNumberMismatch1( intf, message, fromSession );
				break;
			case LSA_REQUEST_RETRANSMIT1:
				result = actionLsaRequestRetransmit1( intf, message, fromSession );
				break;
			case EVENT_BAD_LS_REQ1:
				result = actionEventBadLsReq1( intf, message, fromSession );
				break;
			case LS_SEQUENCE_NUMBER_WRAP:
				result = actionLsSequenceNumberWrap( intf, message, fromSession );
				break;
			case EVENT_BAD_LS_REQ2:
				result = actionEventBadLsReq2( intf, message, fromSession );
				break;
			case DD_RETRANSMIT2:
				result = actionDdRetransmit2( intf, message, fromSession );
				break;
			case DD_RETRANSMIT3:
				result = actionDdRetransmit3( intf, message, fromSession );
				break;
			case DD_RETRANSMIT4:
				result = actionDdRetransmit4( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH2:
				result = actionEventSeqNumberMismatch2( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH3:
				result = actionEventSeqNumberMismatch3( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH4:
				result = actionEventSeqNumberMismatch4( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH5:
				result = actionEventSeqNumberMismatch5( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH6:
				result = actionEventSeqNumberMismatch6( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH7:
				result = actionEventSeqNumberMismatch7( intf, message, fromSession );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH8:
				result = actionEventSeqNumberMismatch8( intf, message, fromSession );
				break;
			case LSA_REQUEST_RETRANSMIT2:
				result = actionLsaRequestRetransmit2( intf, message, fromSession );
				break;
			case REMOVE_LSA_FROM_RETRANSMISSION_LIST:
				result = actionRemoveLsaFromRetransmissionList( intf, message, fromSession );
				break;
			default:
				result = super.push( message, fromSession );
				break;
		}
		
		return result;
	}
	
	/** Action for OLD_LSA_RCPT behavior */
	private boolean actionOldLsaRcpt( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
				
		/* Do not manipulate any packets here - simply wait until there's enough time
		   between the lastNonHelloPacketHeard and now. */
		NeighborState nbr = getNeighbor( message, fromSession );
		if(nbr != null) {
			if(isDDExchangeProcessOver( nbr )) {
			    if (nbr.behaviorState == 0) {
			    	if(ownRouterLSA != null) {
						/* send LSUpdate for the own RouterLSA with sequence number set to 0x70000001 */
						LSA lsaHdr = (LSA)ownRouterLSA.copy();
						lsaHdr.lsSeqnum = 0x70000001;
				
						LS_UpdatePacket upd = new LS_UpdatePacket();
						upd.put(lsaHdr);
					
						String as = "";
						if(ospfSession != null) {
							as = ospfSession.as_nh;
						}
						Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
						ospfPkt.carryPayload( upd );
						
						IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
						newPkt.carryPayload(ospfPkt);
						
						super.push(newPkt, ospfSession);
						log.logMessage("Sent LSUpdate to "+IP_s.IPtoString(nbr.nbrRouterID)+" (Seq.No. 0x70000001).");
						
						if( nbr.timer != null ) {
							nbr.timer.cancel();
						}	
					
						/* start a timer which fires in 2*OSPF.MIN_LS_ARRIVAL */		
						nbr.timer = new BehaviorTimer(inGraph(), OSPF.MIN_LS_ARRIVAL * SSF.Net.Net.seconds(1.0),OLD_LSA_RCPT, nbr);					
						nbr.timer.set(10 * SSF.Net.Net.seconds(1.0));
						nbr.behaviorState = 1;
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}

	/** Action for NBR_STATE_TOO_LOW behavior */
	private boolean actionNbrStateTooLow( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		boolean result = true;
				
		IpHeader ipHeader = (IpHeader)message;
		Packet ospfHeader = (Packet)ipHeader.payload();
		
		if( fromSession instanceof NIC ) {	
			/* wait for the first DD packet
			   when this is received, send an LSUpdate packet.
			   now start a timer whose action method sends an LSRequest */
			
			NeighborState nbr = getNeighbor( message, fromSession );
			if(nbr != null) {
				if(ospfHeader.type == Packet.DATABASE) {
					if (nbr.ddPacketsRcvd == 1) {
						/* GOTCHA! This is the first DD packet! */
						
						if(nbr.behaviorState == 0) {
							LinkInfo link = new LinkInfo(nbr.nbrRouterID, nbr.nbrMask, OSPF.TO_STUB_NET, 0, 4711);
					
							Router_LSA rtLsa = new Router_LSA(false, false, false);
							rtLsa.addLink( link );
					
							LSA lsaHdr = new LSA(options, LSA.ROUTER_LSA, ownRouterID, ownRouterID, ((Host)inGraph()).nhi, 0x80000001);			
							lsaHdr.carryPayload(rtLsa);
					
							LS_UpdatePacket upd = new LS_UpdatePacket();
							upd.put(lsaHdr);
					
							String as = "";
							if(ospfSession != null) {
								as = ospfSession.as_nh;
							}
							Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
							ospfPkt.carryPayload( upd );
					
							IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
							newPkt.carryPayload(ospfPkt);
					
							super.push(newPkt, ospfSession);
							log.logMessage("Sent LSUpdate to "+IP_s.IPtoString(nbr.nbrRouterID)+" (Seq.No. 0x80000001).");
					
							if( nbr.timer != null ) {
								nbr.timer.cancel();
							}
					
							/* wait 10 seconds until sending an LSRequest */
							nbr.timer = new BehaviorTimer(inGraph(), 10 * SSF.Net.Net.seconds(1.0),NBR_STATE_TOO_LOW, nbr);					
							nbr.timer.set(10 * SSF.Net.Net.seconds(1.0));
							nbr.behaviorState = 1;
						}
					
						result = super.push( message, fromSession );
					}
				} else {
					result = super.push( message, fromSession );
				}
			} else {
				/* this case should never occur, because the track method should have
				   created a new entry */
				log.logMessage("DOH! Too many fingers on keyboad error. This should never happen!");
				result = super.push( message, fromSession );
			}
			
		} else {
			if( ospfHeader.type == Packet.DATABASE ) {
				/* Intercept all DD packets coming from the OSPF session so
			  	 * the neighbor will not go into a state greater than ExStart
			  	 */
				drop( ipHeader );  
				log.logMessage("Dropping DD packet from OSPF session.");
				result = false;
			} else {
				/* All other OSPF packets are not of interest. */
				result = super.push( message, fromSession );
			}
		}
		
		return result;
	}
	
	/** Action for DD_RETRANSMIT1 behavior */
	private boolean actionDdRetransmit1( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		boolean drop = false;
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
				/* no DD packets but the initial one are sent to the RUT */
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();
				
				if(ospfHeader.type == Packet.DATABASE) {
					if(nbr.ddPacketsSent > 1) {	
						drop = true;		
						log.logMessage("Dropping non-initial DD packet.");
					}
				}
			}
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop( (IpHeader)message );
			return false;
		}
	}

	/** Action for EVENT_SEQ_NUMBER_MISMATCH1 behavior */
	private boolean actionEventSeqNumberMismatch1( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
				
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
				/* look into the first non-initial DD packet from OSPF */
			
				if(nbr.behaviorState == 0) {
					if(nbr.ddPacketsSent == 2) {	
						IpHeader ipHeader = (IpHeader)message;
						Packet ospfHeader = (Packet)ipHeader.payload();			
			
						if(ospfHeader.type == Packet.DATABASE) {
							
							/* ATTENTION: the order of the option bits is twisted
							 * in the OSPF implementation - the highest order bit
							 * is according to this the 0th bit 
							 */
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* clear the E-bit */
							ddHdr.options[6] = false;
							
							log.logMessage("Cleared E bit in DD packet#2 for"+IP_s.IPtoString(nbr.nbrRouterID)+".");
							
							/* advance the test case state */
							nbr.behaviorState = 1;
						}
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for LSA_REQUEST_RETRANSMIT1 behavior */
	private boolean actionLsaRequestRetransmit1( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		boolean drop=false;
		NeighborState nbr = getNeighbor( message, fromSession );
		if(nbr != null) {
			IpHeader ipHeader = (IpHeader)message;
			Packet ospfHeader = (Packet)ipHeader.payload();		
			
			if(ospfHeader.type == Packet.LSUPDATE) {
				drop = true;
				log.logMessage("Dropping LS update packet.");
			}
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop((IpHeader)message);
			return false;
		}
	}

	/** Action for LSA_REQUEST_RETRANSMIT2 behavior */
	private boolean actionLsaRequestRetransmit2( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		boolean drop=false;
		NeighborState nbr = getNeighbor( message, fromSession );
		if(nbr != null) {
			IpHeader ipHeader = (IpHeader)message;
			Packet ospfHeader = (Packet)ipHeader.payload();		
			
			if(ospfHeader.type == Packet.LSUPDATE) {
				if(nbr.behaviorState == 0) {
					/* the first update is ok*/
					nbr.behaviorState = 1;
				} else {
					drop = true;
					log.logMessage("Dropping LS update packet.");
				}
			}
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop((IpHeader)message);
			return false;
		}
	}

	/** Action for EVENT_BAD_LS_REQUEST1 behavior */
	private boolean actionEventBadLsReq1( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		if(!(fromSession instanceof NIC)) {
			NeighborState nbr = getNeighbor( message, fromSession );
			
			IpHeader ipHdr = (IpHeader)message;
			Packet ospfHdr = (Packet)ipHdr.payload();
			
			if(ospfHdr.type == Packet.LSREQUEST) {
				if(nbr != null) {		
					if (nbr.behaviorState == 0) {		
						
						boolean opts[] = new boolean [8];
						Arrays.fill( opts, false );
						int id = IP_s.StrToInt("47.11.11.47");
						LSA fantasyLsa = new LSA(opts, LSA.ROUTER_LSA, ownRouterID, id, ((Host)inGraph()).nhi, 0x80000001);
											
						LS_RequestPacket lsReq = (LS_RequestPacket)ospfHdr.payload();
   						lsReq.put(fantasyLsa.getKey());
   		
						log.logMessage("Sending LSRequest packet for fantasy LSA to "+nbr.nbrRouterID+".");
						nbr.behaviorState = 1;
					}
				}
			}
		}
	
		return super.push( message, fromSession );
	}
	
	/** Action for LS_SEQUENCE_NUMBER_WRAP behavior */
	private boolean actionLsSequenceNumberWrap( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		/* find the associated neighbor for this packet */		
		NeighborState nbr = getNeighbor( message, fromSession );
		if(nbr != null) {
			/* look, when we've heard the last non hello packet from this neighbor */
			double diff = getSimTime() - nbr.lastNonHelloHeard;
			log.logMessage("Neighbor "+IP_s.IPtoString(nbr.nbrRouterID)+" No non-Hellos heard since: "+diff+".");
			if(diff >= 40.0) { /* 40 seconds should be enough */
				/* have we already sent the manipulated packet? */
			    if (nbr.behaviorState == 0) {
			    	log.logMessage("DD process seems to be over.");
			    	if(nbr.nbrRouterLsa != null) {
						/* send LSUpdate with the neighbor's RouterLSA with sequence number set to 0x7FFFFFFF */
						LSA lsaHdr = (LSA)nbr.nbrRouterLsa.copy();
						lsaHdr.lsSeqnum = 0x7FFFFFFF;
				
						LS_UpdatePacket upd = new LS_UpdatePacket();
						upd.put(lsaHdr);
					
						String as = "";
						if(ospfSession != null) {
							as = ospfSession.as_nh;
						}
						Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
						ospfPkt.carryPayload( upd );
						
						IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
						newPkt.carryPayload(ospfPkt);
						
						super.push(newPkt, ospfSession);
						log.logMessage("Sent LSUpdate to "+IP_s.IPtoString(nbr.nbrRouterID)+" (Seq.No. 0x7fffffff).");
						
						nbr.behaviorState = 1;
					}
				}
			}
		}
		
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_BAD_LS_REQUEST2 behavior */
	private boolean actionEventBadLsReq2( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		boolean drop = false;
		NeighborState nbr = getNeighbor(message, fromSession);
		
		/* wait for the first update from OSPF */
		if(nbr != null) {
			if(getSimTime() > 50.0) {
				if(nbr.behaviorState == 0) {
					if(ownRouterLSA != null) {
						/* send LSUpdate for the own RouterLSA with sequence number set to 0x70000001 */
						LSA lsaHdr = (LSA)ownRouterLSA.copy();
						lsaHdr.lsSeqnum = 0x80000105;
				
						LS_UpdatePacket upd = new LS_UpdatePacket();
						upd.put(lsaHdr);
					
						String as = "";
						if(ospfSession != null) {
							as = ospfSession.as_nh;
						}
						Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
						ospfPkt.carryPayload( upd );
						
						IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
						newPkt.carryPayload(ospfPkt);
						
						super.push(newPkt, ospfSession);
						log.logMessage("Sent LSUpdate to "+IP_s.IPtoString(nbr.nbrRouterID)+" (Seq.No. 0x80000105).");
						
						nbr.behaviorState = 1;
					}
				} else if(nbr.behaviorState == 1) {
						if( getSimTime() > 60.0) {
							shutDownOSPF();
							
							if( nbr.timer != null ) {
								nbr.timer.cancel();
							}
					
							/* wait 10 seconds until sending an LSRequest */
							nbr.timer = new BehaviorTimer(inGraph(), 50 * SSF.Net.Net.seconds(1.0),EVENT_BAD_LS_REQ2, nbr);					
							nbr.timer.set(50 * SSF.Net.Net.seconds(1.0));
							nbr.behaviorState = 2;
						}
				}
			}
			if((nbr.behaviorState == 3)||(nbr.behaviorState == 4)) { 
					
				IpHeader ipHdr = (IpHeader)message;
				Packet ospfHdr = (Packet)ipHdr.payload();
					
				if(ospfHdr.type == Packet.DATABASE) {
					DD_Packet ddPkt = (DD_Packet)ospfHdr.payload();						
							
					Vector lsas = ddPkt.getHeaders();
							
					for(Enumeration e = lsas.elements(); e.hasMoreElements(); ) {
						LSA l = (LSA)e.nextElement();
							
						if((l.lsType == LSA.ROUTER_LSA)
							&&(l.lsID == ownRouterID)) {
							
							if(fromSession instanceof NIC) {
								/* when found manipulate so TR doesn't request */
								l.lsSeqnum = 0x80000001;
								l.lsAge = 2000;
								log.logMessage("Changed LS sequence no. in DD packet for OSPF.");
								nbr.behaviorState++;
							} else {
								/* make sure, the RUT does reqest the LSA */
								l.lsSeqnum = 0x80000205;
								log.logMessage("Changed LS sequence no. in DD packet for Neighbor.");
								nbr.behaviorState++;
							}	
						}
					}
				}
			}
				
		}
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop((IpHeader)message);
			return false;
		}
	}
	
	/** Action for DD_RETRANSMIT2 behavior */
	private boolean actionDdRetransmit2( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		boolean drop = false;
		
		if((getSimTime() > 180.0)) {
			if(nbr != null) {
				if(nbr.behaviorState == 0) {
					
					/* activate special behavior after the link is up again after about 180s */
					nbr.behaviorState = 1;
				}
			}
		}
		
		
		if(fromSession instanceof NIC) {
			if(nbr != null) {
				/* Wait 100 seconds because the TR must build its LSDB to have enough LSAs */
				if(nbr.behaviorState == 1) {
					/* drop the 3rd DD packet from the RUT, so OSPF will retransmit the last
				   	   DD packet */
					IpHeader ipHeader = (IpHeader)message;
					Packet ospfHeader = (Packet)ipHeader.payload();
				
					if(ospfHeader.type == Packet.DATABASE) {
						if(nbr.ddPacketsRcvd == 3) {	
							drop = true;		
							log.logMessage("Dropping 3rd DD packet from neighbor.");
						}
					}
				}
			}
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop( (IpHeader)message );
			return false;
		}
	}
	
	/** Action for DD_RETRANSMIT3 behavior */
	private boolean actionDdRetransmit3( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		boolean drop = false;
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
				/* no DD packets but the initial and the first non-initial one are sent 
				   to the RUT */
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();
				
				if(ospfHeader.type == Packet.DATABASE) {
					if(nbr.ddPacketsSent > 2) {	
						drop = true;		
						log.logMessage("Dropping DD packet #"+nbr.ddPacketsSent);
					}
				}
			}
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop( (IpHeader)message );
			return false;
		}
	}
	
	/** Action for DD_RETRANSMIT4 behavior */
	private boolean actionDdRetransmit4( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if((getSimTime() > 180.0)) {
			if(nbr != null) {
				if(nbr.behaviorState == 0) {
					
					/* activate special behavior after the link is up again after about 180s */
					nbr.behaviorState = 1;
					
					nbr.lastMBitSent = true;
					nbr.lastMBitRcvd = true;
				}
			}
		}
		
		if(nbr != null) {
			if(nbr.behaviorState == 1) {
				if(!(fromSession instanceof NIC)) {
					/* clear the contents of all DD packets */
					IpHeader ipHeader = (IpHeader)message;
					Packet ospfHeader = (Packet)ipHeader.payload();
					
					if(ospfHeader.type == Packet.DATABASE) {
						
						DD_Packet ddPkt = (DD_Packet)ospfHeader.payload();
						Vector lsaHdrs = ddPkt.getHeaders();
						/* ATTENTION: getHeaders returns a reference to the Headers vector.
						   If this behavior is changed in the future, this must be taken into
						   account. Then a new DD packet must be generated and inserted into the
						   OSPF packet instead of the existing one */
						lsaHdrs.clear();
						log.logMessage("Cleared DD packet for neighbor.");
					}
				}
			
				/* when DD process has been finished wait for 5 seconds */
				if((nbr.lastMBitSent == false)&&(nbr.lastMBitRcvd == false)) {
					
					nbr.timer = new BehaviorTimer(inGraph(), 5 * SSF.Net.Net.seconds(1.0),DD_RETRANSMIT4, nbr);					
					nbr.timer.set(5 * SSF.Net.Net.seconds(1.0));
					nbr.behaviorState = 2;
					log.logMessage("DD process complete - waiting for 20 seconds.");
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH2 behavior */
	private boolean actionEventSeqNumberMismatch2( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
			
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();			
			
				if(nbr.behaviorState == 0) {
					/* look into the first two non-initial DD packet from OSPF */
					if((nbr.ddPacketsSent == 2)||(nbr.ddPacketsSent == 3)) {	
			
						if(ospfHeader.type == Packet.DATABASE) {
							
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* set the M-bit */
							ddHdr.more = true;
							
							log.logMessage("Set the M bit in DD packet.");
							/* advance the test case state */
							if(nbr.ddPacketsSent == 3) {
								nbr.behaviorState = 1;
							}
						}
					}
				}
				if(nbr.behaviorState == 1) {
					if(nbr.ddPacketsSent == 4) {
						
						/* the third non-initial DD packet */
						if(ospfHeader.type == Packet.DATABASE) {
														
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* set the I-bit */
							ddHdr.initialize = true;
							
							log.logMessage("Set the I bit in DD packet.");
							/* advance the test case state */
							nbr.behaviorState = 2;
						}
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH3 behavior */
	private boolean actionEventSeqNumberMismatch3( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
			
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();			
			
				if(nbr.behaviorState == 0) {
					/* look into the first two non-initial DD packet from OSPF */
					if((nbr.ddPacketsSent == 2)||(nbr.ddPacketsSent == 3)) {	
			
						if(ospfHeader.type == Packet.DATABASE) {
														
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* set the M-bit */
							ddHdr.more = true;
							
							log.logMessage("Set the M bit in DD packet.");
							/* advance the test case state */
							if(nbr.ddPacketsSent == 3) {
								nbr.behaviorState = 1;
							}
						}
					}
				}
				if(nbr.behaviorState == 1) {
					if(nbr.ddPacketsSent == 4) {
						
						/* the third non-initial DD packet */
						if(ospfHeader.type == Packet.DATABASE) {
														
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* increase the sequence number by 1, so it is higher than expected */
							ddHdr.DD_seq_num += 1;
							
							log.logMessage("Increased SeqNo. in DD packet.");
							/* advance the test case state */
							nbr.behaviorState = 2;
						}
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH4 behavior */
	private boolean actionEventSeqNumberMismatch4( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
			
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();			
			
				if(nbr.behaviorState == 0) {
					/* look into the first two non-initial DD packet from OSPF */
					if((nbr.ddPacketsSent == 2)||(nbr.ddPacketsSent == 3)) {	
			
						if(ospfHeader.type == Packet.DATABASE) {
														
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* set the M-bit */
							ddHdr.more = true;
							
							log.logMessage("Set the M bit in DD packet.");
							/* advance the test case state */
							if(nbr.ddPacketsSent == 3) {
								nbr.behaviorState = 1;
							}
						}
					}
				}
				if(nbr.behaviorState == 1) {
					if(nbr.ddPacketsSent == 4) {
						
						/* the third non-initial DD packet */
						if(ospfHeader.type == Packet.DATABASE) {
														
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* decrease the sequence number by 2, so it is lower than expected */
							ddHdr.DD_seq_num -= 2;
							
							log.logMessage("Decreased SeqNo. in DD packet.");
							/* advance the test case state */
							nbr.behaviorState = 2;
						}
					}
				}
			}
		}	
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH5 behavior */
	private boolean actionEventSeqNumberMismatch5( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
				/* look into the first non-initial DD packet from OSPF */
			
				if(nbr.behaviorState == 0) {
					if(nbr.ddPacketsSent == 2) {	
						IpHeader ipHeader = (IpHeader)message;
						Packet ospfHeader = (Packet)ipHeader.payload();			
			
						if(ospfHeader.type == Packet.DATABASE) {
							
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							/* clear the MS-bit */
							ddHdr.isMaster = false;
							
							log.logMessage("Cleared the MS in DD packet.");
							/* advance the test case state */
							nbr.behaviorState = 1;
						}
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH6 behavior */
	private boolean actionEventSeqNumberMismatch6( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		/* find the associated neighbor for this packet */		
		NeighborState nbr = getNeighbor( message, fromSession );
		if(nbr != null) {
			/* look, when we've heard the last non hello packet from this neighbor */
			double diff = getSimTime() - nbr.lastNonHelloHeard;
			log.logMessage("Neighbor "+IP_s.IPtoString(nbr.nbrRouterID)+" No non-Hellos heard since: "+diff+".");
			if(diff >= 50.0) { /* 40 seconds should be enough */
				/* have we already sent the manipulated packet? */
			    if (nbr.behaviorState == 0) {
			    	log.logMessage("DD process seems to be over for longer than RouterDeadInterval.");
			    	
			    	/* create new DD packet */
			    	DD_Packet ddHdr =  new DD_Packet(nbr.ddMTU, nbr.ddOptions, false, false, nbr.ddMaster, nbr.ddSequence+1);
			    	
			    	String as = "";
					if(ospfSession != null) {
						as = ospfSession.as_nh;
					}
					Packet ospfPkt = new Packet(Packet.DATABASE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
					ospfPkt.carryPayload( ddHdr );
						
					IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
					newPkt.carryPayload(ospfPkt);
						
					super.push(newPkt, ospfSession);
					log.logMessage("Sent DD packet to "+IP_s.IPtoString(nbr.nbrRouterID)+".");
			    	
					nbr.behaviorState = 1;	
				}
			}
		}
				
		return super.push( message, fromSession );
	}
	
	/** Action for EVENT_SEQ_NUMBER_MISMATCH7 behavior */
	private boolean actionEventSeqNumberMismatch7( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(!(fromSession instanceof NIC)) {
			if(nbr != null) {
				/* look into the first non-initial DD packet from OSPF */
			
				if(nbr.behaviorState == 0) {
					if(nbr.ddPacketsSent == 2) {	
						IpHeader ipHeader = (IpHeader)message;
						Packet ospfHeader = (Packet)ipHeader.payload();			
			
						if(ospfHeader.type == Packet.DATABASE) {
							
							DD_Packet ddHdr = (DD_Packet)ospfHeader.payload(); 
							
							/* add a fantasy-LSA header to the DD-Packet (unknown LS-Type)*/
							/* modification of the own router LSA is not a good idea here because
							   it maybe that this has not been discovered yet. so we construct
							   a completely new lsa... */
							/*LSA lsaHdr = (LSA)ownRouterLSA.copy();*/
							boolean opts[] = new boolean [8];
							Arrays.fill( opts, false );
							
							/* 0x7f is an arbitrary LS type that doesn't exist yet */
							LSA lsaHdr = new LSA(opts, (byte)0x7f, ownRouterID, ownRouterID, ((Host)inGraph()).nhi, 0x80000001);
							ddHdr.addLSAHeader(lsaHdr);
							
							log.logMessage("Added LSA of unknown type to DD packet.");
							/* advance the test case state */
							nbr.behaviorState = 1;
						}
					}
				}
			}
		}
				
		return super.push( message, fromSession );
	}

	/** Action for EVENT_SEQ_NUMBER_MISMATCH8 behavior */
	private boolean actionEventSeqNumberMismatch8( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		/* Not implemented yet - AS external LSAs are not supported yet */
				
		return super.push( message, fromSession );
	}
	
	/** Action for REMOVE_LSA_FROM_RERANSMISSION_LIST behavior */
	private boolean actionRemoveLsaFromRetransmissionList( int intf, ProtocolMessage message, ProtocolSession fromSession )
			throws ProtocolException {
		
		boolean drop = false;
		
		NeighborState nbr = getNeighbor( message, fromSession );
		
		if(nbr != null) {
			if( fromSession instanceof NIC ) {
				IpHeader ipHeader = (IpHeader)message;
				Packet ospfHeader = (Packet)ipHeader.payload();			
			
				if( nbr.behaviorState != 0 ) {
					if((ospfHeader.type == Packet.LSUPDATE)||
					   (ospfHeader.type == Packet.LSACK)) {
						/* drop all updates and acks when the fake updates were sent */
						drop = true;
					}
				}
			}

			double time = getSimTime();
						
			if(nbr.behaviorState == 0) {

				if(time >= 40.0) {
					/* send first fake packet */
					LSA lsaHdr = (LSA)ownRouterLSA.copy();
					lsaHdr.lsSeqnum++;
					
					Router_LSA rt = (Router_LSA)lsaHdr.payload();
					LinkInfo li = new LinkInfo(	IP_s.StrToInt("47.11.11.0"),
												IP_s.StrToInt("255.255.255.0"),
												3,
												1);
					rt.addLink(li);
				
					LS_UpdatePacket upd = new LS_UpdatePacket();
					upd.put(lsaHdr);
					
					String as = "";
					if(ospfSession != null) {
						as = ospfSession.as_nh;
					}
					Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
					ospfPkt.carryPayload( upd );
						
					IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
					newPkt.carryPayload(ospfPkt);
				
					try {			
						super.push(newPkt, ospfSession);
					} catch(Exception e) {
						e.printStackTrace();
					}	
					log.logMessage("Sent LSUpdate with fake stub network to "+IP_s.IPtoString(nbr.nbrRouterID)+".");
					nbr.behaviorState = 1;
				} 
			}
			
			if(nbr.behaviorState == 1) {

				if(time >= 60.0) {
					/* send second fake packet */
					LSA lsaHdr = (LSA)ownRouterLSA.copy();
					lsaHdr.lsSeqnum +=2 ;
				
					LS_UpdatePacket upd = new LS_UpdatePacket();
					upd.put(lsaHdr);
					
					String as = "";
					if(ospfSession != null) {
						as = ospfSession.as_nh;
					}
					Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, nbr.areaId);
					ospfPkt.carryPayload( upd );
						
					IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,nbr.ifIP,nbr.nbrIP,1);
					newPkt.carryPayload(ospfPkt);
				
					try {			
						super.push(newPkt, ospfSession);
					} catch(Exception e) {
						e.printStackTrace();
					}	
					log.logMessage("Sent LSUpdate without fake stub network to "+IP_s.IPtoString(nbr.nbrRouterID)+".");
					nbr.behaviorState = 2;
				} 
			}
			
		}
				
		if(!drop) {
			return super.push( message, fromSession );
		} else {
			drop( (IpHeader)message );
			return false;
		}
	}
	
	/** Find the associated interface for a ProtocolMessage */
	private int associatedInterface( ProtocolMessage message, ProtocolSession fromSession ) {
		
		int ai = -1; /* no interface associated with the packet */
		
		/* first test, if the packet comes from a nic or not. */
		if( fromSession instanceof NIC ) {
			/* this is the easy case - get the NIC's id and return it. */
			ai = ((NIC)fromSession).ID;
		} else {
			/* this is the harder case. we must lookup, over which interface
			   the packet would be sent by IP */
			IpHeader ipHeader = (IpHeader)message;
			RoutingInfo rtgInfo = ROUTING_TABLE.findBest(ipHeader.SOURCE_IP,ipHeader.DEST_IP);
			
			if(rtgInfo != null) {
				NIC use_iface = rtgInfo.next_hop_interface();
				ai = use_iface.ID;
			}
		}
		
		/* at this point, ai should not be -1 anymore - perhaps add some
		   error handling for this, like an exception which is thrown if
		   no interface is found. For the moment simply return -1, if this
		   happens. */
		
		return ai;
	}
	
	/** Lookup the neighbor associated with a specific packet */
	private NeighborState getNeighbor(ProtocolMessage message, ProtocolSession fromSession) {
		
		NeighborState result = null;
		
		IpHeader ipHdr = (IpHeader)message;
		if(fromSession instanceof NIC) {
			/* when the packet comes from a NIC, the neighbor's router ID can be found
			   in the OSPF header */
			Packet ospfHdr = (Packet)ipHdr.payload();
			if( ospfHdr != null ) {
				result = (NeighborState)neighbors.get(new Integer(ospfHdr.router_ID));
			}
		} else {
			/* when the packet is originated locally we have slighty more work to do */
			
			/* first look at the IP header and determine the destination address */
			Integer da = new Integer(ipHdr.DEST_IP);
			
			/* look if the address is contained in the addresses hashtable */
			if( addresses.containsKey( da )) {
			
				/* if yes get the associated router id */
				Integer ri = (Integer)addresses.get( da );
			
				if( ri != null) {
					/* determine the associated router neighbors structure */
					result = (NeighborState)neighbors.get(ri);
				}
			}
		}
		
		return result;
	}
	
	/** Returns the special behavior specified for an interface */	  
	private int getBehavior(int i) {
		
		int result = DEFAULT;
		
		if(behavior.size() > i) {
			Integer r = (Integer)behavior.get( i );
		
			if( r != null ) {
				result = r.intValue();
			}
		}

		return result;
	}
	
	/** Gets the behavior id from a behavior string.*/
    private int behaviorFromString( String b ) {
        	
       	int bid = DEFAULT;
        	
       	for(int i = 0; i < behaviorLabel.length; i++) {
        	if (b.equals(behaviorLabel[i]))	{
        		bid = i;
        		break;
        	}
        }
        	
        return bid;
    }
    
    /** Dispatch the timer events and call the correc handler */
    private void timerEvent( int behavior, Object data ) {
    	switch( behavior ) {
    		case OLD_LSA_RCPT:
				eventOldLsaRcpt( data );
				break;
			case NBR_STATE_TOO_LOW:
				eventNbrStateTooLow( data );
				break;
			case DD_RETRANSMIT1:
				eventDdRetransmit1( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH1:
				eventEventSeqNumberMismatch1( data );
				break;
			case LSA_REQUEST_RETRANSMIT1:
				eventLsaRequestRetransmit1( data );
				break;
			case EVENT_BAD_LS_REQ1:
				eventEventBadLsReq1( data );
				break;
			case LS_SEQUENCE_NUMBER_WRAP:
				eventLsSequenceNumberWrap( data );
				break;
			case EVENT_BAD_LS_REQ2:
				eventEventBadLsReq2( data );
				break;
			case DD_RETRANSMIT2:
				eventDdRetransmit2( data );
				break;
			case DD_RETRANSMIT3:
				eventDdRetransmit3( data );
				break;
			case DD_RETRANSMIT4:
				eventDdRetransmit4( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH2:
				eventEventSeqNumberMismatch2( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH3:
				eventEventSeqNumberMismatch3( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH4:
				eventEventSeqNumberMismatch4( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH5:
				eventEventSeqNumberMismatch5( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH6:
				eventEventSeqNumberMismatch6( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH7:
				eventEventSeqNumberMismatch7( data );
				break;
			case EVENT_SEQ_NUMBER_MISMATCH8:
				eventEventSeqNumberMismatch8( data );
				break;
			case LSA_REQUEST_RETRANSMIT2:
				eventLsaRequestRetransmit2( data );
				break;
			case REMOVE_LSA_FROM_RETRANSMISSION_LIST:
				eventRemoveLsaFromRetransmissionList( data );
				break;	
			default:
				break;
    	}
    	
    }
    
    /** Timer callback for OLD_LSA_RCPT */
    private void eventOldLsaRcpt( Object data ) {
    	/* when this timer fires, a new RouterLSA is sent to the neighbor - 
    	   this time using sequence no 0x8FFFFFFE */
    	NeighborState assNeighbor = (NeighborState) data;
    	
    	if(assNeighbor.behaviorState == 1) {
    		
			if(ownRouterLSA != null) {
				/* send LSUpdate for the own RouterLSA with sequence number set to 0x70000001 */
				LSA lsaHdr = (LSA)ownRouterLSA.copy();
				lsaHdr.lsSeqnum = 0x8FFFFFFE;
				
				LS_UpdatePacket upd = new LS_UpdatePacket();
				upd.put(lsaHdr);
					
				String as = "";
				if(ospfSession != null) {
					as = ospfSession.as_nh;
				}
				Packet ospfPkt = new Packet(Packet.LSUPDATE, ownRouterID, ((Host)inGraph()).nhi, as, assNeighbor.areaId);
				ospfPkt.carryPayload( upd );
						
				IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,assNeighbor.ifIP,assNeighbor.nbrIP,1);
				newPkt.carryPayload(ospfPkt);
				
				try {			
					super.push(newPkt, ospfSession);
				} catch(Exception e) {
					e.printStackTrace();
				}	
				log.logMessage("Sent LSUpdate to "+IP_s.IPtoString(assNeighbor.nbrRouterID)+".");
			}
    	}
    }
    
    /** Timer callback for NBR_STATE_TOO_LOW */
    private void eventNbrStateTooLow( Object data ) {
    	
    	NeighborState assNeighbor = (NeighborState) data;
    	
    	/* when this timer fires, send an LS Request packet to the neighbor, requesting its
    	   router LSA */
    	if(assNeighbor.behaviorState == 1) {
    		LSA nbrRtLSAHdr = new LSA(options, LSA.ROUTER_LSA,assNeighbor.nbrRouterID,assNeighbor.nbrRouterID,"",0x80000001);
    		LS_RequestPacket lsReq = new LS_RequestPacket();
    		lsReq.put(nbrRtLSAHdr.getKey());
    	
    		String as = "";
			if(ospfSession != null) {
				as = ospfSession.as_nh;
			}
			Packet ospfPkt = new Packet(Packet.LSREQUEST, ownRouterID, ((Host)inGraph()).nhi, as, assNeighbor.areaId);
			ospfPkt.carryPayload( lsReq );
				
			IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,assNeighbor.ifIP,assNeighbor.nbrIP,1);
			newPkt.carryPayload(ospfPkt);
		
			try {			
				super.push(newPkt, ospfSession);
			} catch(Exception e) {
				e.printStackTrace();
			}
			
			log.logMessage("Sent LSRequest packet to "+IP_s.IPtoString(assNeighbor.nbrRouterID)+".");
		}
	}

	/** Timer callback for DD_RETRANSMIT1 */
	private void eventDdRetransmit1( Object data ) {
		/* nothing to do */
	}

	/** Timer callback for EVENT_SEQ_NUMBER_MISMATCH1 */
    private void eventEventSeqNumberMismatch1( Object data ) {
    	/* nothing to do */
    }

	/** Timer callback for LSA_REQUEST_RETRANSMIT1 */
	private void eventLsaRequestRetransmit1( Object data ) {
		/* nothing to do */
    }

	/** Timer callback for LSA_REQUEST_RETRANSMIT2 */
	private void eventLsaRequestRetransmit2( Object data ) {
		/* nothing to do */
    }

	/** Timer callback for EVENT_BAD_LS_REQ1 */
	private void eventEventBadLsReq1( Object data ) {
		/* nothing to do */
    }
    
    /** Timer callback for LS_SEQUENCE_NUMBER_WRAP */
    private void eventLsSequenceNumberWrap( Object data ) {
		/* nothing to be done here - that's easy =;) */
    }
    
    private void eventEventBadLsReq2( Object data ) {
		
		NeighborState assNeighbor = (NeighborState)data;
    	
    	if(assNeighbor.behaviorState == 2) {
    		try {
    			reStartOSPF();
    		} catch (Exception e) {
    			log.errMessage("Cannot restart OSPF!");
    			e.printStackTrace();
    		}
    		
    		
    		assNeighbor.behaviorState = 3;
		}
    }
    
    /** Timer callback for DD_RETRANSMIT1 */
    private void eventDdRetransmit2( Object data ) {
    	/* nothing to do */
	}
	
	/** Timer callback for DD_RETRANSMIT1 */
	private void eventDdRetransmit3( Object data ) {
		/* nothing to do */
	}
	
	/** Timer callback for DD_RETRANSMIT1 */
	private void eventDdRetransmit4( Object data ) {
		
		NeighborState assNeighbor = (NeighborState) data;
    	
    	/* when this timer fires, send the last sent DD packet to the neighbor */
    	if(assNeighbor.behaviorState == 2) {
    		
    		DD_Packet ddPkt = (DD_Packet)assNeighbor.lastDDPacket.copy();
    	
    		Vector lsaHdrs = ddPkt.getHeaders();
			lsaHdrs.clear();
    	
    		String as = "";
			if(ospfSession != null) {
				as = ospfSession.as_nh;
			}
			Packet ospfPkt = new Packet(Packet.DATABASE, ownRouterID, ((Host)inGraph()).nhi, as, assNeighbor.areaId);
			ospfPkt.carryPayload( ddPkt );
				
			IpHeader newPkt = new IpHeader(Protocols.OSPF_PRTL_NUM,assNeighbor.ifIP,assNeighbor.nbrIP,1);
			newPkt.carryPayload(ospfPkt);
		
			try {			
				super.push(newPkt, ospfSession);
			} catch(Exception e) {
				e.printStackTrace();
			}
			
			log.logMessage("Retransmitted last DD packet to "+IP_s.IPtoString(assNeighbor.nbrRouterID)+".");
		}
	}
	
	/** Timer callback for EVENT_SEQ_NUMBER_MISMATCH2 */
	private void eventEventSeqNumberMismatch2( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for EVENT_SEQ_NUMBER_MISMATCH3 */
    private void eventEventSeqNumberMismatch3( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for EVENT_SEQ_NUMBER_MISMATCH4 */
    private void eventEventSeqNumberMismatch4( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for EVENT_SEQ_NUMBER_MISMATCH5 */
    private void eventEventSeqNumberMismatch5( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for EVENT_SEQ_NUMBER_MISMATCH6 */
    private void eventEventSeqNumberMismatch6( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for EVENT_SEQ_NUMBER_MISMATCH7 */
    private void eventEventSeqNumberMismatch7( Object data ) {
    	/* nothing to do */
    }

	/** Timer callback for EVENT_SEQ_NUMBER_MISMATCH8 */
	private void eventEventSeqNumberMismatch8( Object data ) {
    	/* nothing to do */
    }
    
    /** Timer callback for REOVE_LSA_FROM_RETRANSMISSION_LIST */
    private void eventRemoveLsaFromRetransmissionList( Object data ) {
		/* nothing to do */
    }
    
    /** Shuts the associated OSPF session down. */
    private void shutDownOSPF() {
    	
    	if( ospfSession != null ) {
    		log.logMessage("Shutting down OSPF.");
    		ospfSession.stop();
    	}
    }
    
    /** (Re-)Starts the associated OSPF session. */
    private void reStartOSPF() throws ProtocolException {
    	
    	if( ospfSession != null ) {
    		log.logMessage("(Re-)Starting OSPF.");
    		ospfSession.init();
    	}
    }
    	
    /** Determines if the DD process is over */
    private boolean isDDExchangeProcessOver(NeighborState nbr) {
    	
    	boolean result = false;
    	
    	if(nbr != null) {
    		double diff = getSimTime() - nbr.lastNonHelloHeard;
			log.logMessage("Neighbor "+IP_s.IPtoString(nbr.nbrRouterID)+". No non-Hellos heard since: "+diff+".");
			if(diff >= 40.0) { /* 40 seconds should be enough */	
				log.logMessage("Database exchange with "+IP_s.IPtoString(nbr.nbrRouterID)+" seems to be over.");
				result = true;
			}
		}
    	
    	return result;
    }	
    	
	/** Return the actual simulation time in seconds. */
	private double getSimTime() {
		
		return inGraph().now()/((double)SSF.Net.Net.seconds(1.0));
	}
}